package com.msb.mall.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.msb.common.constant.SeckillConstant;
import com.msb.common.utils.R;
import com.msb.mall.seckill.dto.SeckillSessionDTO;
import com.msb.mall.seckill.dto.SeckillSkuRedisDTO;
import com.msb.mall.seckill.dto.SeckillSkuRelationDTO;
import com.msb.mall.seckill.dto.SkuInfoDTO;
import com.msb.mall.seckill.feign.CouponFeignService;
import com.msb.mall.seckill.feign.ProductFeignService;
import com.msb.mall.seckill.service.SeckillSkuScheduleService;
import org.apache.skywalking.apm.toolkit.trace.Trace;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.time.*;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class SeckillSecheduleServiceImpl implements SeckillSkuScheduleService {

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ProductFeignService productFeignService;

    @Trace
    @Override
    public void uploadSeckillSku3Day() {
        // 明天要执行的活动信息
        R r = couponFeignService.getLates3DaySession();
        if (r.getCode() == 0){
            String json = JSON.toJSONString(r.get("data"));
            List<SeckillSessionDTO> seckillSessionList = JSON.parseObject(json, new TypeReference<List<SeckillSessionDTO>>() {
            });
            // 上架商品
            // redis保存活动信息
            saveSeckillSession(seckillSessionList);

            // redis保存活动的商品信息
            saveSeckillSku(seckillSessionList);

        }



    }

    /**
     * 活动场次缓存进redis
     * @param seckillSessionList
     */
    private void saveSeckillSku(List<SeckillSessionDTO> seckillSessionList) {

        // 循环获取每一个活动场次
        for (SeckillSessionDTO seckillSessionDTO : seckillSessionList) {
            // 过期时间
            long time = new Date().getTime();
            LocalTime max = LocalTime.MAX;
            // date 转 localDate
            Date endTime = seckillSessionDTO.getEndTime();
            Instant instant = endTime.toInstant();
            ZoneId zoneId = ZoneId.systemDefault();
            LocalDate localDate = instant.atZone(zoneId).toLocalDate();
            LocalDateTime maxDate = LocalDateTime.of(localDate, max);
            long maxTime = Timestamp.valueOf(maxDate).getTime();
            Long expire = maxTime - time;
            // 缓存redis的key
            String key = SeckillConstant.SESSION_CHACE_PREFIX + seckillSessionDTO.getStartTime().getTime()+"_"+seckillSessionDTO.getEndTime().getTime();
            Boolean flag = redisTemplate.hasKey(key);
            if (!flag){
                List<String> list = seckillSessionDTO.getSeckillSkuRelationList().stream().map(skuRelation -> {
                    return seckillSessionDTO.getId() + "_" + skuRelation.getSkuId();
                }).collect(Collectors.toList());
                redisTemplate.opsForList().leftPushAll(key,list);
                redisTemplate.expire(key,expire,TimeUnit.MILLISECONDS);
            }
        }
    }

    /**
     * 参加活动的商品信息缓存到redis
     * @param seckillSessionList
     */
    private void saveSeckillSession(List<SeckillSessionDTO> seckillSessionList) {

        for (SeckillSessionDTO seckillSessionDTO : seckillSessionList) {
            // hash缓存
            String key = SeckillConstant.SKU_CHACE_PREFIX;
            BoundHashOperations<String, Object, Object> ops = redisTemplate.boundHashOps(key);
            for (SeckillSkuRelationDTO sku : seckillSessionDTO.getSeckillSkuRelationList()) {
                String skuKey = sku.getPromotionSessionId() + "_" + sku.getSkuId();
                Boolean flag = ops.hasKey(skuKey);
                if (!flag) {
                    Long skuId = sku.getSkuId();
                    // 获取商品信息
                    R r = productFeignService.info(skuId);
                    SeckillSkuRedisDTO dto = new SeckillSkuRedisDTO();
                    if (r.getCode() == 0) {
                        String skuInfo = JSON.toJSONString(r.get("skuInfo"));
                        SkuInfoDTO skuInfoDTO = JSON.parseObject(skuInfo, SkuInfoDTO.class);
                        dto.setSkuInfo(skuInfoDTO);
                    }
                    dto.setSeckillCount(sku.getSeckillCount());
                    dto.setSeckillPrice(sku.getSeckillPrice());
                    dto.setSeckillLimit(sku.getSeckillLimit());
                    dto.setSkuId(sku.getSkuId());
                    dto.setSeckillSort(sku.getSeckillSort());
                    dto.setStartTime(seckillSessionDTO.getStartTime().getTime());
                    dto.setEndTime(seckillSessionDTO.getEndTime().getTime());
                    dto.setPromotionSessionId(sku.getPromotionSessionId());
                    // 随机码
                    String randCode = UUID.randomUUID().toString().replace("-", "");
                    dto.setRandCode(randCode);
                    // 过期时间
                    long time = new Date().getTime();
                    LocalTime max = LocalTime.MAX;
                    // date 转 localDate
                    Date endTime = seckillSessionDTO.getEndTime();
                    Instant instant = endTime.toInstant();
                    ZoneId zoneId = ZoneId.systemDefault();
                    LocalDate localDate = instant.atZone(zoneId).toLocalDate();
                    LocalDateTime maxDate = LocalDateTime.of(localDate, max);
                    long maxTime = Timestamp.valueOf(maxDate).getTime();
                    Long expire = maxTime - time;
                    // 分布式信号量，作用: 限流
                    RSemaphore semaphore = redissonClient.getSemaphore(SeckillConstant.SKU_STOCK_SEMAPHORE + randCode);
                    // 把秒杀活动的商品数量作为分布式信号量的信号量
                    semaphore.trySetPermits(sku.getSeckillCount().intValue());
                    semaphore.expire(expire, TimeUnit.MILLISECONDS);
                    ops.put(skuKey, JSON.toJSONString(dto));
                    // 商品过期时间，活动当天晚上12点清理
                    ops.expire(expire, TimeUnit.MILLISECONDS);
                }

            }

        }

    }
}
